"""
Copyright 2012 Wieser Dietmar, Margit Mutschlechner.

This file is part of Multimedia Social Networking Platform.

Multimedia Social Networking Platform is free software: 
you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Multimedia Social Networking Platform is distributed
in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Multimedia Social Networking Platform.  
If not, see <http://www.gnu.org/licenses/>.

"""

import random
import time
from msnp.service import network
from msnp.service.network import sending
from msnp.service.network.model import Model
from msnp.logger import Logger
import os
from threading import Thread


__tFirstRingReport = 450 #after 7min 30sec first ring report
__tPrintRing = 580 #after 9min 40sec print ring
__tSecondRingReport = 950 #after 15min 50sec second ring report

__probDieDuringJoin = 10 # die during join with probability 1/10, else live
__probLeaveDuringJoin = 2 # if I should die during join, leave during join with probability 1/2, else kill

__probDie = 5 # die with probability 1/5, else live
__probLeave = 2 # if I should die, leave with probability 1/2, else kill

__maxGroupsCreate = 4 # create 0-4 groups
__maxGroupsJoin = 5 # send 0-5 times a search for all groups (at callback decide if join group)
__probJoinGroup = 5 # join 1 group of all groups received in a callback with probability 1/5

def killMyself():
    pid = os.getpid()
    Logger.info("I was killed. Pid=" + str(pid))
    os.system("kill -9 " + str(pid))
    
def leaveMyself():
    Logger.info("I leave the network")
    network.LeaveRequest.requestFinish(True)

def joinGroup(groups):
    if groups != None and len(groups) > 0:
        probJoinGroup = random.randint(1, __probJoinGroup)
        if probJoinGroup == 1:
            g = groups.pop()
            groupname = g.getGroupname()
            ownername = g.getOwnername()
            Logger.info("Join group " + groupname)
            network.NetworkController.addAsMember(groupname, ownername)


def createOrJoinGroup():
    global __maxGroupsCreate
    global __maxGroupsJoin
    global __tSecondRingReport
        
    numberCreateGroups = random.randint(1, __maxGroupsCreate)
    numberJoinGroups = random.randint(0, __maxGroupsJoin)
    sleeptime = random.randint(__tSecondRingReport, __tSecondRingReport + 60)
    time.sleep(sleeptime)
    while numberCreateGroups > 0:
        myName = network.NetworkController.getMySelf().getMyIdentity().getName()
        groupname = myName + "-group" + str(numberCreateGroups)
        Logger.info("Create group " + groupname)
        network.NetworkController.establishGroup(groupname, None, None, None)
        numberCreateGroups -= 1
    time.sleep(5)
    while numberJoinGroups > 0:
        # search for all groups, at callback-method become member of one of the callbacked groups
        #network.NetworkController.getAllGroups()
        numberJoinGroups -= 1

def killOrLeaveDuringJoin():
    global __probDieDuringJoin
    global __probLeaveDuringJoin
        
    die = random.randint(1, __probDieDuringJoin)
    if die == 1:
        sleeptime = random.randint(30, 40)
        time.sleep(sleeptime)
    
        leave = random.randint(1, __probLeaveDuringJoin)
        if leave == 1:
            leaveMyself()
        else:
            killMyself()

def killOrLeave():
    global __probDie
    global __tPrintRing
    global __probLeave
        
    die = random.randint(1, __probDie)
    timeToSleep = random.randint(__tPrintRing - 80, __tPrintRing)
    time.sleep(timeToSleep)
        
    #print fingertable
    if network.NetworkController.getMySelf().isLowLevelPeer():
        Logger.info('I am a low level peer. My responsible peer is ' + network.NetworkController.getMySelf().getResponsibleSuperPeer().getNameBundle())
    else:
        predecessor = network.NetworkController.getMySelf().getPredecessor()
        Logger.info("fingertable:\n" + network.NetworkController.getMySelf().fingerToString()) 
        Logger.info('predecessor: ' + (predecessor.getNameBundle() if predecessor != None else 'None'))
        
    if die == 1:
        leave = random.randint(1, __probLeave)
        if leave == 1 :
            leaveMyself()
        else:
            killMyself()

def validateRing():
    global __tFirstRingReport
    global __tPrintRing
    global __tSecondRingReport
        
    time.sleep(__tFirstRingReport)
#        time.sleep(600)
    myName = network.NetworkController.getMySelf().getMyIdentity().getName()
        #first ring report
    Logger.info("before first ring report")
    ringReportEntry = network.NetworkValidator.generateRingReportEntry()
    sending.Sender.forwardRingReport([ringReportEntry], {}, {}, [myName], Model.RingReportType.PRINT_RING)
    sending.Sender.forwardRingReport([ringReportEntry], {}, {}, [myName], Model.RingReportType.VALIDATE_RING)
    Logger.info("after first ring report")
        #send ringreport so that peers recognize that some crashed
    time.sleep(__tPrintRing - __tFirstRingReport)
    Logger.info("before print ring")
    ringReportEntry = network.NetworkValidator.generateRingReportEntry()
    sending.Sender.forwardRingReport([ringReportEntry], {}, {}, [myName], Model.RingReportType.PRINT_RING)
    Logger.info("after print ring")
    #second ring report
    time.sleep(__tSecondRingReport - __tPrintRing - __tFirstRingReport)
    Logger.info("before second ring report")
    ringReportEntry = network.NetworkValidator.generateRingReportEntry()
    sending.Sender.forwardRingReport([ringReportEntry], {}, {}, [myName], Model.RingReportType.PRINT_RING)
    sending.Sender.forwardRingReport([ringReportEntry], {}, {}, [myName], Model.RingReportType.VALIDATE_RING)
    Logger.info("after second ring report")

                
#################
##   THREADS   ##
#################

class CreateOrJoinGroupThread(Thread):
    def __init__(self):
        Thread.__init__(self)
        
    def run(self):
        createOrJoinGroup()

class KillOrLeaveDuringJoinThread(Thread):
    def __init__(self):
        Thread.__init__(self)
        
    def run(self):
        killOrLeaveDuringJoin()
                
class KillOrLeaveThread(Thread):
    def __init__(self):
        Thread.__init__(self)
        
    def run(self):
        killOrLeave()
        
class ValidateRingThread(Thread):
    def __init__(self):
        Thread.__init__(self)
        
    def run(self):
        validateRing()
